﻿//This filter is tested and seems to work correctly

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// This is another corner detector i created on my own.
    /// </summary>
    public class Corner2 :  BaseForeBackgroundColorFilter, ICornerFinder
    {
        protected bool m_bIntegrate = false;
        protected List<Point> m_aPoint = new List<Point>();

        /// <summary>
        /// Initializes a new instance of the <see cref="Corner2"/> class.
        /// </summary>
        public Corner2()
            : this(false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Corner2"/> class.
        /// </summary>
        /// <param name="_bIntegrate">if set to <c>true</c> [_b integrate].</param>
        public Corner2(bool _bIntegrate)
        {
            m_bIntegrate = _bIntegrate;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Corner2"/> class.
        /// </summary>
        /// <param name="_corner2">The _corner2.</param>
        internal Corner2(Corner2 _corner2)
        {
            this.m_bIntegrate = _corner2.m_bIntegrate;
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="Corner2"/> is integrate.
        /// </summary>
        /// <value><c>true</c> if integrate; otherwise, <c>false</c>.</value>
        public bool Integrate
        {
            get { return m_bIntegrate; }
            set { m_bIntegrate = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            List<Point> aPoint = Definitions.EnvironmentPointListClockwise;

            UnsafeBitmap bitmap = _bitmap;
            Color[] aColor = new Color[8];
            int[] aSum = new int[8];

            int nWhite = 0;
            int nBlack = 0;

            int y = 0;
            int x = 0;
            int i = 0;
            Color color = Color.Transparent;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            //Variable m_bIntegrate determines if we add found edges to existing bitmap,
            //or if none edges are displayed black and edges in red.

            for (y = 1; y < nHeight - 1; y++)
            {
                for (x = 1; x < nWidth - 1; x++)
                {
                    color = bitmap.GetPixel(x, y);

                    aSum[0] = (int)ColorFunctions.Distance(aColor[7], aColor[0]);
                    for (i = 1; i < 8; i++)
                    {
                        aColor[i] = bitmap.GetPixel(x + aPoint[i].X, y + aPoint[i].Y);
                        aSum[i] = (int)ColorFunctions.Distance(aColor[i - 1], aColor[i]);
                    }
                    bool bIsEdgeCandidate = IsEdgeCandidate(aSum);
                    if (m_bIntegrate)
                    {
                        bitmap.SetPixel(x, y, bIsEdgeCandidate ? m_ColorForeground : m_ColorBackground);
                    }
                    else 
                    {
                        if (bIsEdgeCandidate)
                        {
                            m_aPoint.Add(new Point(x, y));
                        }
                    }
                }
            }

            if (!m_bIntegrate)
            {
                for (y = 1; y < nHeight; y++)
                {
                    for (x = 1; x < nWidth; x++)
                    {
                        bitmap.SetPixel(x, y, m_ColorBackground);
                    }
                }
                for (i = 0; i < m_aPoint.Count; i++)
                {
                    bitmap.SetPixel(m_aPoint[i].X, m_aPoint[i].Y, m_ColorForeground);
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Sums the specified a sum.
        /// </summary>
        /// <param name="aSum">A sum.</param>
        /// <returns></returns>
        protected int Sum(int[] aSum)
        {
            int nSum = 0;
            foreach (int nTemp in aSum)
            {
                nSum += nTemp;
            }
            return nSum;
        }

        /// <summary>
        /// Count char _chChar in String
        /// </summary>
        /// <param name="_sString">The _s string.</param>
        /// <param name="_chChar">The _CH char.</param>
        /// <returns></returns>
        protected int CountChar(string _sString, char _chChar)
        {
            int nCount = 0;
            foreach (char ch in _sString)
            {
                if (ch == _chChar)
                    nCount++;
            }
            return nCount;
        }

        /// <summary>
        /// Test if we got an Edge Candidate
        /// </summary>
        /// <param name="aSum">A sum.</param>
        /// <returns>
        /// 	<c>true</c> if [is edge candidate] [the specified a sum]; otherwise, <c>false</c>.
        /// </returns>
        protected bool IsEdgeCandidate(int[] aSum)
        {
            bool bFound = false;
            int nAboveZero = 0;

            if (aSum[0] > 0) nAboveZero++;

            string sText = string.Empty;
            int i = 0;

            int nSumCount = aSum.Length;
            for (i = 1; i < nSumCount; i++)
            {
                if (Math.Abs(aSum[i] - aSum[i - 1]) < 100 && aSum[i] > 0)
                {
                    sText += "1";
                }
                else
                {
                    sText += "0";
                }
            }
            return sText.Contains("111") && CountChar(sText,'1') < 7 && CountChar(sText,'0')>3;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Corner2(this);
        }

        /// <summary>
        /// Get return points
        /// </summary>
        public List<Point> Points
        {
            get { return m_aPoint; }
        }
    }
}
